package com.leesche.netdoll.communication.protocol.decoder;

import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.leesche.framework.core.codec.EndianUtil;
import com.leesche.framework.core.lang.ArrayUtil;
import com.leesche.framework.core.lang.ReflectUtil;
import com.leesche.netdoll.communication.protocol.SimpleMessage;
import com.leesche.netdoll.communication.protocol.decoder.DecoderConfiguration.Mapper;

public abstract class AbstractDecoder<T extends SimpleMessage> implements Decoder<T> {

	private static final Logger logger = LogManager.getLogger(AbstractDecoder.class);

	protected final List<Mapper> mappers;

	protected AbstractDecoder(List<Mapper> mappers) {
		this.mappers = mappers;
	}

	@Override
	public T decode(byte[] src, DecoderContext ctx) {

		T o = ReflectUtil.newGenericInstance(this.getClass());

		o.setDeviceId(ctx.getDeviceId());
		o.setCode(ctx.getCode());

		parseHeader(o, src);

		if (mappers != null && mappers.size() > 0) {

			for (Mapper mapper : mappers) {

				int offset = mapper.getOffset();
				int len = mapper.getLength();
				int endIndex = offset + len;

				if (src.length > offset && src.length >= endIndex) {
					byte[] content = ArrayUtil.subarray(src, offset, endIndex);

					mapping(o, mapper.getField(), mapper.getResolver(), content);
				}
			}
		}

		return o;
	}

	private void mapping(final T o, final String field, final String resolver, final byte[] content) {

		try {

			Resolver<?> res = ResolverManager.newInstance().resolver(resolver);

			if (res != null) {
				Object value = res.resolve(content);

				ReflectUtil.setFieldValue(o, field, value);
			}

		} catch (ResolverNotFountException e) {
			logger.catching(e);
		}
	}

	private void parseHeader(final T o, final byte[] src) {
		o.setLength(EndianUtil.decodeUnsignedShortLE(new byte[] { src[1], src[2] }));
		o.setDsc(src[4] & 0xff);
		o.setHsc(src[5] & 0xff);
		o.setDest(src[6] & 0xff);
		o.setSrc(src[7] & 0xff);
		o.setType(((src[8] & 0xff) >> 6) & 0xff);
		o.setSeq(EndianUtil.decodeUnsignedShortLE(new byte[] { src[9], src[10] }));
	}

}
